using System;
using System.Collections.Generic;
using System.Data;
using System.ServiceModel;
using System.Threading;
using LanSunStudio.Core.Database;
using LanSunStudio.ExClassLib.HLASimulation.Federation;
using LanSunStudio.ExClassLib.MessageExtention;
using LanSunStudio.ExClassLib.SingleAgentContract;
namespace LanSunStudio.Service.SingleAgent
{
    /// <summary>
    /// Beer Service Implementation
    /// </summary>
    /// <remarks>
    /// Single is the default concurrency mode, but it never hurts to be explicit.
    /// Note the single threading model still functions properly with use of one way methods for callbacks 
    /// since they are marked as one way on the contract.
    /// </remarks>
    [ServiceBehavior(
        ConcurrencyMode = ConcurrencyMode.Multiple,
        InstanceContextMode = InstanceContextMode.Single)]
    public class SingleAgentService : ISingleAgentContract
    {
        //public Assembly AsmWFEdit = Assembly.LoadFrom("WorkFlowLib.dll");
        #region ---------------------Variable---------------------
        // NOTE: The variables for storing callbacks and beer inventory are static.
        //       This is necessary since the service is using PerCall instancing.
        //       An instance of the service will be created each time a service method is invoked by a client.
        //       Consequently, the state must be persisted somewhere in between calls.

        private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        public log4net.ILog m_log_out;
        private static Dictionary<string, ISingleAgentContractCallBack> m_AgentcallbackList =
            new Dictionary<string, ISingleAgentContractCallBack>();
        MessageEx msgRequest = new MessageEx();
        public Dictionary<string, object> MessageList;
        public static AutoResetEvent waitHandle;
        private string m_ThisCallBackPath;
        public string ThisCallBackPath
        {
            get { return m_ThisCallBackPath; }
            set { m_ThisCallBackPath = value; }
        }
        List<ProcessPoint> m_listProcessPoint = new List<ProcessPoint>();
        public List<ProcessPoint> ListProcessPoint
        {
            get { return m_listProcessPoint; }
            set { m_listProcessPoint = value; }
        }
        #endregion

        public SingleAgentService()
        {
            m_ThisCallBackPath = "MA0001";
            m_log_out = m_log;
            MessageList = new Dictionary<string, object>();
            waitHandle = new AutoResetEvent(false);
            try
            {
            }
            catch (Exception ex)
            {
                m_log_out.Error(ex);
            }
        }

        #region ---------------------Contract---------------------
        string ISingleAgentContract.Subscribe()
        {
            ISingleAgentContractCallBack callback =
                OperationContext.Current.GetCallbackChannel<ISingleAgentContractCallBack>();
            string m_AgentPath = Guid.NewGuid().ToString().ToUpper();
            try
            {
                if (callback != null)
                {
                    lock (m_AgentcallbackList)
                    {
                        m_AgentcallbackList.Add(m_AgentPath, callback);
                    }
                }
            }
            catch (Exception ex)
            {
                m_log.Error(ex);
            }
            return m_AgentPath;
        }
        void RegisterAgent(MessageEx msgRequest)
        {
            ISingleAgentContractCallBack callback =
                OperationContext.Current.GetCallbackChannel<ISingleAgentContractCallBack>();
            string m_AgentPath = msgRequest.Address;
            try
            {
                if (callback != null)
                {
                    lock (m_AgentcallbackList)
                    {
                        m_AgentcallbackList.Add(m_AgentPath, callback);
                    }
                }
            }
            catch (Exception ex)
            {
                m_log.Error(ex);
            }
        }
        void ISingleAgentContract.Unsubscribe(string m_AgentPath)
        {
            lock (m_AgentcallbackList)
            {
                if (m_AgentcallbackList.ContainsKey(m_AgentPath))
                {
                    m_AgentcallbackList.Remove(m_AgentPath);
                }
            }
        }
        void ISingleAgentContract.KeepConnection()
        {
            // Do nothing.
        }
        MessageEx ISingleAgentContract.SendSAMessage(MessageEx msgRequest)
        {
            string JobID = msgRequest.JobID;
            switch (JobID)
            {
                case "OnJoinFederation":
                    RegisterAgent(msgRequest);
                    msgRequest.JobID = "OnJoinedFederation";
                    SendMessage(msgRequest.Address, msgRequest);
                    break;
                default:
                    BroadcastMessage(msgRequest);
                    break;
            }
            return msgRequest;
        }
        /// <summary>
        /// Notifies the clients of messages.
        /// </summary>
        /// <param name="clientId">Identifies the client that sent the message.</param>
        /// <param name="message">The message to be sent to all connected clients.</param>
        private void BroadcastMessage(MessageEx msgRequest)
        {
            // Call each client's callback method
            string m_AgentPath = msgRequest.Address;
            ThreadPool.QueueUserWorkItem
                (
                delegate
                {
                    lock (m_AgentcallbackList)
                    {
                        List<string> disconnectedClientGuids = new List<string>();
                        foreach (KeyValuePair<string, ISingleAgentContractCallBack> client in m_AgentcallbackList)
                        {
                            try
                            {
                                client.Value.HandleSAMessage(msgRequest);
                            }
                            catch (Exception)
                            {
                                disconnectedClientGuids.Add(client.Key);
                            }
                        }
                        foreach (string clientGuid in disconnectedClientGuids)
                        {
                            m_AgentcallbackList.Remove(clientGuid);
                        }
                    }
                }
            );
        }
        public void RegisterMessage(MessageEx TheMessage)
        {
            if (TheMessage.MessageID_Return != "" || TheMessage.WFMessageID_Req != "")
            {
                if (!MessageList.ContainsKey(TheMessage.MessageID))
                {
                    MessageList.Add(TheMessage.MessageID, TheMessage);
                }
            }
        }
        private void SendMessage(string AgentPath, MessageEx MsgReturn)
        {
            try
            {
                MsgReturn.Address = AgentPath;
                ISingleAgentContractCallBack AgentCallback =
                    (ISingleAgentContractCallBack)m_AgentcallbackList[AgentPath];
                AgentCallback.HandleSAMessage(MsgReturn);
            }
            catch (Exception ex)
            {
                m_log.Error(ex);
            }
        }
        #endregion

        #region ---------------------Method---------------------
        private void SendMessage2ProcessPoint(MessageEx msgRequest)
        {
            PlanTask newjob;
            try
            {
                DataSet setRequest = msgRequest.setRequest;
                string ProcessPointKey = setRequest.Tables[0].Rows[0]["ProcessPointKey"].ToString();
                int index = m_listProcessPoint.FindIndex(p => p.ProcessPointKey == ProcessPointKey);
                if (index > 0)
                {
                    newjob = new PlanTask(msgRequest);
                    m_listProcessPoint[index].ResourceThread.AddTask(newjob);
                }
            }
            catch (Exception ex)
            {
                m_log.Error(ex);
            }
        }
        private void BuildProcessPoint()
        {
            DataSet setReturn = new DataSet();
            try
            {
                setReturn = (DataSet)DataAccessService.Instance.GetDataBySysID("HLAProcessPoint");
                ProcessPoint m_PPTemp = new ProcessPoint();
                m_listProcessPoint = m_PPTemp.NewProcessPointByTable(m_ThisCallBackPath,
                    setReturn.Tables["HLAProcessPoint"]);
            }
            catch (Exception ex)
            {
                m_log.Error(ex);
            }
        }
        #endregion
    }
}
